/*
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */

'use strict';

const leftVideo = document.getElementById('leftVideo');
const rightVideo = document.getElementById('rightVideo');

let stream;

let pc1;
let pc2;
const offerOptions = {
  offerToReceiveAudio: 1,
  offerToReceiveVideo: 1,
};

let startTime;

class WSConnection {
  constructor() {
    this._onReceiveOffer = null;
    this._onReceiveAnswer = null;
    this._onIceCandidate = null;
    this._onGetAllPlayers = null;
  }

  connect(url, name) {
    return new Promise((resolve) => {
      this._ws = new WebSocket(url);
      this._ws.onmessage = (event) => this._onMessage(event);
      this._ws.onopen = () => {
        this.sendToServer({ type: 'handShake', name });
        resolve();
      };
    });
  }

  sendToServer(data) {
    this._ws?.send(JSON.stringify({ to: '__server__', data }));
  }

  sendToPeer(name, data) {
    this._ws?.send(JSON.stringify({ to: name, data }));
  }

  sendOffer(name, offer) {
    this.sendToPeer(name, { type: 'offer', offer });
  }

  sendAnswer(name, answer) {
    this.sendToPeer(name, { type: 'answer', answer });
  }

  sendIceCandidate(name, iceCandidate) {
    this.sendToPeer(name, { type: 'iceCandidate', iceCandidate });
  }

  set onReceiveOffer(callback) {
    this._onReceiveOffer = callback;
  }

  set onIceCandidate(callback) {
    this._onIceCandidate = callback;
  }

  set onReceiveAnswer(callback) {
    this._onReceiveAnswer = callback;
  }

  set onGetAllPlayers(callback) {
    this._onGetAllPlayers = callback;
  }

  _onMessage(event) {
    try {
      const data = JSON.parse(event.data);
      switch (data.type) {
        case 'allPlayers':
          this._onGetAllPlayers && this._onGetAllPlayers(data.players);
          break;
        case 'offer':
          this._onReceiveOffer && this._onReceiveOffer(data.offer);
          break;
        case 'answer':
          this._onReceiveAnswer && this._onReceiveAnswer(data.answer);
          break;
        case 'iceCandidate':
          this._onIceCandidate && this._onIceCandidate(data.iceCandidate);
          break;
        default:
          break;
      }
    } catch (err) {
      console.error('receive message error', err);
    }
  }
}

const ws = new WSConnection();
const wsPull = new WSConnection();

function maybeCreateStream() {
  if (stream) {
    return;
  }
  if (leftVideo.captureStream) {
    stream = leftVideo.captureStream();
    console.log('Captured stream from leftVideo with captureStream', stream);
    call();
  } else if (leftVideo.mozCaptureStream) {
    stream = leftVideo.mozCaptureStream();
    console.log(
      'Captured stream from leftVideo with mozCaptureStream()',
      stream
    );
    call();
  } else {
    console.log('captureStream() not supported');
  }
}

// Video tag capture must be set up after video tracks are enumerated.
leftVideo.oncanplay = maybeCreateStream;
if (leftVideo.readyState >= 3) {
  // HAVE_FUTURE_DATA
  // Video is already ready to play, call maybeCreateStream in case oncanplay
  // fired before we registered the event handler.
  maybeCreateStream();
}

rightVideo.onloadedmetadata = () => {
  console.log(
    `Remote video videoWidth: ${this.videoWidth}px,  videoHeight: ${this.videoHeight}px`
  );
};

rightVideo.onresize = () => {
  console.log(
    `Remote video size changed to ${rightVideo.videoWidth}x${rightVideo.videoHeight}`
  );
  // We'll use the first onresize callback as an indication that
  // video has started playing out.
  if (startTime) {
    const elapsedTime = window.performance.now() - startTime;
    console.log('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
    startTime = null;
  }
};

async function createOffer() {
  const offer = await pc1.createOffer(offerOptions);
  await pc1.setLocalDescription(offer);
  ws.sendOffer('pull', offer);
}

async function initWs() {
  await ws.connect('ws://127.0.0.1:8079', 'push');

  ws.onReceiveAnswer = (answer) => pc1.setRemoteDescription(answer);

  ws.onIceCandidate = (candidate) => {
    try {
      console.log('add icecandidate ok, playit');
      leftVideo.play();
      pc1.addIceCandidate(candidate);
    } catch (err) {
      console.error('addIceCandidate', err);
    }
  };

  ws.onGetAllPlayers = (players) => {
    if (players.indexOf('pull') >= 0) {
      console.log('pull peer connected');
      createOffer();
    }
  };
}

async function initPc1() {
  pc1 = new RTCPeerConnection();
  console.log('Created local peer connection object pc1');
  pc1.onicecandidate = (e) => {
    if (!e.candidate) return;
    ws.sendIceCandidate('pull', e.candidate);
  };
  // pc1.oniceconnectionstatechange = (e) => onIceStateChange(pc1, e);
  stream.getTracks().forEach((track) => pc1.addTrack(track, stream));
  console.log('Added local stream to pc1');
}

async function call() {
  console.log('Starting call');
  startTime = window.performance.now();
  const videoTracks = stream.getVideoTracks();
  const audioTracks = stream.getAudioTracks();
  if (videoTracks.length > 0) {
    console.log(`Using video device: ${videoTracks[0].label}`);
  }
  if (audioTracks.length > 0) {
    console.log(`Using audio device: ${audioTracks[0].label}`);
  }

  await initWs();
  await initPc1();
}
